/**
 *  Copyright 2002-2009 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package foo.bar.blog.core.domain;

import foo.bar.blog.core.security.PermissionType;
import foo.bar.blog.core.security.RoleType;
import org.apache.openjpa.persistence.jdbc.ElementForeignKey;
import org.apache.openjpa.persistence.jdbc.ElementJoinColumn;

import javax.persistence.*;
import java.util.*;

/**
 * Represents a {@link foo.bar.blog.core.domain.Blog}'s User.
 *
 * @author tmjee
 * @version $Date: 2009-06-06 13:26:52 +0000 (Sat, 06 Jun 2009) $ $Id$
 */
@Entity
@EntityListeners({EntityAuditListener.class})
@Table(name="TBL_USER")
@NamedQueries({
    @NamedQuery(name = "User_findByUsername",
                query = "select user from User as user where user.username = :username"),
    @NamedQuery(name ="User_findByUserId",
                query = "select user from User as user where user.id = :userId"),
    @NamedQuery(name="User_countByUsername",
                query="select count(user) from User as user where user.username= :username "),
    @NamedQuery(name="User_findAllUsers",
                query="select user from User as user"),
    @NamedQuery(name="User_countAllUsers",
                query="select count(user) from User as user"),
    @NamedQuery(name="User_findByUsernameAndEmail",
                query="select user from User as user where user.username = :username and user.email = :email") 
})
public class User implements Identifiable  {

    // Data will be setup inb SetupService upon initialization
    public static User ANONYMOUS_USER;


    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "userIdGenerator")
    @TableGenerator(name="userIdGenerator", table = "TBL_ID_GENERATOR",
                    pkColumnName = "ID", valueColumnName = "ID_VALUE",
                    pkColumnValue = "TBL_USER_CURRENT_ID", initialValue = 1,
                    allocationSize = 10)
    @Column(name="ID", nullable = false)
    private long id;


    @OneToMany(targetEntity = Property.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @ElementJoinColumn(name = "USER_ID", referencedColumnName = "ID")
    @ElementForeignKey
    private Set<Property> properties = new LinkedHashSet<Property>();


    @OneToMany(targetEntity = Role.class, cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @ElementJoinColumn(name="USER_ID", referencedColumnName = "ID")
    @ElementForeignKey
    private Set<Role> roles = new LinkedHashSet<Role>();


    @OneToMany(targetEntity = Permission.class, cascade=CascadeType.ALL, fetch = FetchType.EAGER)
    @ElementJoinColumn(name="USER_ID", referencedColumnName = "ID")
    @ElementForeignKey
    private Set<Permission> permissions = new LinkedHashSet<Permission>();


    @Basic
    @Column(name = "USERNAME")
    private String username;

    @Basic
    @Column(name = "EMAIL")
    private String email;

    @Basic
    @Column(name="PASSWORD")
    private String password;

    @Basic
    @Column(name="LOCKED")
    private boolean locked;

    @Basic
    @Column(name="DISABLED")
    private boolean disabled;

    @Basic
    @Column(name="EXPIRY_DATE")
    private Date expiryDate;



    @ManyToMany(targetEntity = Blog.class, mappedBy = "members")
    private Set<Blog> membershipBlogs = new LinkedHashSet<Blog>();

    @OneToMany(targetEntity = Blog.class, mappedBy = "owner")
    private Set<Blog> ownershipBlogs = new LinkedHashSet<Blog>();


    @Transient
    private boolean needsPasswordEncryption = false;


    public Set<Blog> getMembershipBlogs() {
        return membershipBlogs;
    }

    public void setMembershipBlogs(Set<Blog> membershipBlogs) {
        this.membershipBlogs = membershipBlogs;
    }

    public Set<Blog> getOwnershipBlogs() {
        return ownershipBlogs;
    }

    public void setOwnershipBlogs(Set<Blog> ownershipBlogs) {
        this.ownershipBlogs = ownershipBlogs;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Set<Property> getProperties() {
        return properties;
    }

    public void setProperties(Set<Property> properties) {
        this.properties = properties;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public Set<Permission> getPermissions() {
        return permissions;
    }

    public void setPermissions(Set<Permission> permissions) {
        this.permissions = permissions;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        needsPasswordEncryption = true;
        this.password = password;
    }

    boolean needsPasswordEncryption() {
        return needsPasswordEncryption;
    }
    void resetNeedsPasswordEncryption() {
        needsPasswordEncryption = false;
    }


    public boolean isLocked() {
        return locked;
    }

    public void setLocked(boolean locked) {
        this.locked = locked;
    }

    public boolean isDisabled() {
        return disabled;
    }

    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }

    public Date getExpiryDate() {
        return expiryDate;
    }

    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }

    public boolean isCredentialExpired() {
        return (((expiryDate != null) && (new Date().compareTo(expiryDate)) >= 0));
    }



   //////////////////////////////////////////////////////////////////////////////////

    public boolean hasRole(RoleType roleType) {
        for (Role role : roles) {
            if (role.getType().equals(roleType)) {
                return true;
            }
        }
        return false;
    }
    public void removeRole(RoleType roleType) {
        for (Iterator i = roles.iterator(); i.hasNext(); ) {
            Role role = (Role)i.next();
            if (role.getType().equals(roleType)) {
                i.remove();
            }
        }
    }
    public Role getRole(RoleType roleType) {
        for (Role role : roles) {
            if (role.getType().equals(roleType)) {
                return role;
            }
        }
        return null;
    }


    public boolean hasPermission(PermissionType permissionType) {
        for(Permission permission : permissions) {
            if (permission.getType().equals(permissionType))  {
                return true;
            }
        }
        return false;
    }
    public void removePermission(PermissionType permissionType) {
        for (Iterator i = permissions.iterator(); i.hasNext();) {
            Permission permission = (Permission) i.next();
            if (permission.getType().equals(permissionType)) {
                i.remove();
            }
        }
    }
    public Permission getPermission(PermissionType permissionType) {
        for(Permission permission : permissions) {
            if (permission.getType().equals(permissionType))  {
                return permission;
            }
        }
        return null;
    }



    public Property getProperty(String propertyName) {
        if (propertyName != null && (propertyName.trim().length() > 0)) {
            for (Property property : properties) {
                if (propertyName.equals(property.getName().trim())) {
                    return property;
                }
            }
        }
        return null;
    }


    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (username != null ? !username.equals(user.username) : user.username != null) return false;

        return true;
    }

    public int hashCode() {
        int result;
        result = (int) (id ^ (id >>> 32));
        result = 31 * result + (properties != null ? properties.hashCode() : 0);
        result = 31 * result + (roles != null ? roles.hashCode() : 0);
        result = 31 * result + (permissions != null ? permissions.hashCode() : 0);
        result = 31 * result + (username != null ? username.hashCode() : 0);
        result = 31 * result + (password != null ? password.hashCode() : 0);
        result = 31 * result + (locked ? 1 : 0);
        result = 31 * result + (disabled ? 1 : 0);
        result = 31 * result + (expiryDate != null ? expiryDate.hashCode() : 0);
        return result;
    }
}
